grammar Dice;

options {
	output = AST;
	ASTLabelType = CommonTree;
}

tokens {
	SEMI	= ';' ;
	EACH	= '@' ;
	PLUS 	= '+' ;
	MINUS	= '-' ;
	MULT	= '*' ;
	DIV	= '/' ;
	D	= 'd' ;
	CD	= 'D' ;
	QUESTION = '?' ;
	DOT 	= '.' ;
	COMMA	= ',' ;
	COLON 	= ':' ;
	LPAREN	= '(' ;
	RPAREN 	= ')' ;
	LBRACE  = '{' ;
	RBRACE  = '}' ;
	HASH    = '#' ;
}

@header{
package com.area42games.grammar;
}

@lexer::header{
package com.area42games.grammar;
}

@members {
List<Object> expressions = new ArrayList<Object>();
}

/*------------------------------------------------------------------
 * PARSER RULES
 *------------------------------------------------------------------*/

exprList
	 : (expr SEMI!)+
	 ;
	
expr 
	: altExpr (EACH^ altExpr)?
//	| IDENTIFIER EQ^ expr 
	;

altExpr
	: comparison ( QUESTION^ caselist | QUESTION^ thenElse)?
	;

thenElse : altExpr COLON^ altExpr;

eachExpr
	: factor EACH^ altExpr ;
 
caselist
	: LBRACE^ caseexpr (COMMA! caseexpr)* RBRACE!;

caseexpr
	: range COLON expr -> ^(HASH range expr)
	| expr
	;

range : NUMBER (DOT^ DOT! NUMBER)?;

comparison : addsub (COMP^ addsub)? ;

addsub	
	: term? ( ( PLUS^ | MINUS^ )  term )* ;

term	: multdiv (QUALIFIER)? ;
	
multdiv	: factor ( ( MULT^ | DIV^ ) factor )* ;

factor	: reference ( (D | CD)^ reference (opts)* )? ;

opts 	: HIGH^ reference
	| LOW^ reference
	| REROLL^ range
	| ROLLAGAIN^ range
	| caselist
	;
	
reference 
	: LPAREN^ altExpr RPAREN!
	| IDENTIFIER
	| NUMBER
	;
	

/*------------------------------------------------------------------
 * LEXER RULES
 *------------------------------------------------------------------*/

EQ : '=' ;

COMP 	: '<' | '>' | '==' | '<=' | '>=' ;

NUMBER	: (DIGIT)+ ;

QUALIFIER : '[' (LETTER | DIGIT)+ ']' ;

IDENTIFIER : '$' '!'? (LETTER | DIGIT)+ ;

HIGH : 'high' | 'hi' ;

LOW : 'low' | 'lo';

REROLL : 'reroll' | 're' ;

ROLLAGAIN : 'rollagain' | 'ra' ;

WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ 	{ $channel = HIDDEN; } ;

fragment DIGIT	: '0'..'9' ;
fragment LETTER : 'a'..'z' | 'A'..'Z' ;
